#include "messageshowarea.h"
#include<QScrollBar>
#include<QVBoxLayout>
#include"model/data.h"
#include"debug.h"
#include"mainwidget.h"
#include"userinfowidget.h"
#include"./model/datacenter.h"
#include"toast.h"
#include"soundrecorder.h"

#include<QGridLayout>
#include<QPushButton>
#include<QLabel>
#include<QPainter>
#include<QPainterPath>
#include<QTimer>
#include<QFileDialog>
#include<QMenu>

MessageShowArea::MessageShowArea()
{
    //1.初始化基本属性
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    this->setWidgetResizable(true);

    //2.设置滚动条样式
    this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(240,240,240);}");
    this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0;}");
    this->setStyleSheet("QScrollArea {border: none;}");

    //3.创建Container这样的widget，作为包含内部元素的容器
    container = new QWidget();
    this->setWidget(container);

    //4.给container添加布局管理器
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setSpacing(0);
    layout->setContentsMargins(0,0,0,0);
    container->setLayout(layout);

//构造测试数据
#if TEST_UI
    model::UserInfo userInfo;
    userInfo.userId = QString::number(1000);
    userInfo.nickname = "张三";
    userInfo.phone = QString::number(18912345678);
    userInfo.description = QString("从今天开始好好敲代码");
    userInfo.avatar = QIcon(":/resource/image/defaultAvatar.png");
    model::Message message = model::Message::makeMessage(model::TEXT_TYPE,"",userInfo,QString("这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息").toUtf8(),"");

    for(int i = 0;i < 30;i++)
    {
        model::UserInfo userInfo;
        userInfo.userId = QString::number(i);
        userInfo.phone = QString("18912345678");
        userInfo.description = QString("从今天开始好好敲代码");
        userInfo.nickname = "张三" + QString::number(i);
        userInfo.avatar = QIcon(":/resource/image/defaultAvatar.png");
        model::Message message = model::Message::makeMessage(model::TEXT_TYPE,"",userInfo,(QString("这是一条测试消息") + QString::number(i)).toUtf8(),"");
        this->BackaddWidget(true,message);
    }

    this->FontaddWidget(false,message);
#endif
}

void MessageShowArea::BackaddWidget(bool isLeft, const model::Message &message)
{
    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message);
    container->layout()->addWidget(messageItem);
}

void MessageShowArea::FontaddWidget(bool isLeft, const model::Message &message)
{
    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft,message);
    //这里前插需要使用layout的子类QVBoxLayout的成员函数，需要强转一下类型
    QVBoxLayout* layout = dynamic_cast<QVBoxLayout*>(container->layout());
    layout->insertWidget(0,messageItem);
}

void MessageShowArea::clear()
{
    //遍历布局管理器，删除里面的元素
    QLayout* layout = container->layout();
    for(int i = layout->count() - 1;i>=0;i--)
    {
        QLayoutItem* item = layout->takeAt(i);
        if(item != nullptr && item->widget() != nullptr)
        {
            delete item->widget();
        }
    }
}

void MessageShowArea::scrollToEnd()
{
    //拿到滚动区域中的垂直滚动条
    //获取滚动条的最大值
    //根据最大值，设置滚动条的滚动位置
    //为了使滚动效果更佳，能够在界面绘制好之后进行滚动条的设置
    //给这里的滚动操作，加上个"延时"

    QTimer* timer = new QTimer();
    connect(timer,&QTimer::timeout,this,[=](){
       //获取到垂直滚动条的最大值
        int maxValue = this->verticalScrollBar()->maximum();
       //设置滚动条的滚动位置
        this->verticalScrollBar()->setValue(maxValue);

        timer->stop();
        timer->deleteLater();
    });
    timer->start(500);
}

MessageItem::MessageItem(bool isleft)
    :isLeft(isleft)
{
    //不需要其他的行为
}

MessageItem *MessageItem::makeMessageItem(bool isleft, const model::Message &message)
{
    //1.创建对象和布局管理器
    MessageItem* messageItem = new MessageItem(isleft);
    QGridLayout* layout = new QGridLayout();
    layout->setSpacing(10);
    layout->setContentsMargins(30,10,30,0);
    messageItem->setMinimumHeight(100);
    messageItem->setLayout(layout);

    //2.创建头像
    QPushButton* avatarBtn = new QPushButton();
    avatarBtn->setFixedSize(40,40);
    avatarBtn->setIconSize(QSize(40,40));
    avatarBtn->setIcon(message.sender.avatar);
    avatarBtn->setStyleSheet("QPushButton{border: none;}");

    if(isleft)
    {
        layout->addWidget(avatarBtn,0,0,2,1,Qt::AlignTop | Qt::AlignLeft);
    }
    else
    {
        layout->addWidget(avatarBtn,0,1,2,1,Qt::AlignTop | Qt::AlignLeft);
    }

    //3.创建名字和时间
    QLabel* nameLabel = new QLabel();
    nameLabel->setText(message.sender.nickname + "|" + message.time);
    nameLabel->setAlignment(Qt::AlignBottom);
    nameLabel->setStyleSheet("QLabel { font-size: 12px; color: rgb(178,178,178)}");
    if(isleft)
    {
        layout->addWidget(nameLabel,0,1);
    }
    else
    {
        layout->addWidget(nameLabel,0,0,Qt::AlignRight);
    }

    //创建消息体
    QWidget* contentWidget = new QWidget();
    switch (message.messageType) {
    case model::TEXT_TYPE:
        contentWidget = makeTextMessageItem(isleft,message.content);
        break;
    case model::IMAGE_TYPE:
        contentWidget = makeImageMessageItem(isleft,message.fileId,message.content);
        break;
    case model::FILE_TYPE:
        contentWidget = makeFileMessageItem(isleft,message);
        break;
    case model::SPEECH_TYPE:
        contentWidget = makeSpeechMessageItem(isleft,message);
        break;
    default:
        LOG() << "错误的消息类型！messageType=" << message.messageType;
        break;
    }
    if(isleft)
    {
        layout->addWidget(contentWidget,1,1);
    }
    else
    {
        layout->addWidget(contentWidget,1,0);
    }

    //5.连接信号槽，处理用户点击头像的操作
    connect(avatarBtn,&QPushButton::clicked,messageItem,[=](){
        MainWidget* mainWidget = MainWidget::getInstance();
        UserInfoWidget* userInfoWidget = new UserInfoWidget(message.sender,mainWidget);
        userInfoWidget->exec();
    });

    //6.当用户修改了昵称的时候同步修改此处的用户昵称
    if(!isleft){
        //只是针对右侧消息(自己的消息)做下列操作
        model::DataCenter* dataCenter = model::DataCenter::getInstance();
        connect(dataCenter,&model::DataCenter::changeNicknameDone,messageItem,[=]{
            nameLabel->setText(dataCenter->getMyself()->nickname + "|" + message.time);
        });

        connect(dataCenter,&model::DataCenter::changeAvatarDone,messageItem,[=](){
            model::UserInfo* myself = dataCenter->getMyself();
            avatarBtn->setIcon(myself->avatar);
        });

    }

    return messageItem;
}

QWidget *MessageItem::makeSpeechMessageItem(bool isLeft,const model::Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[语音]",isLeft,message.messageType
                                                                       ,message.fileId,message.content);
    return messageContentLabel;
}

QWidget *MessageItem::makeImageMessageItem(bool isLeft , const QString& fileId , const QByteArray& content)
{
    MessageImageLabel* messageImageLabel = new MessageImageLabel(fileId,content,isLeft);
    return messageImageLabel;
}

QWidget *MessageItem::makeFileMessageItem(bool isLeft,const model::Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[文件]" + message.fileName,isLeft,message.messageType
                                                                       ,message.fileId,message.content);
    return messageContentLabel;
}

QWidget *MessageItem::makeTextMessageItem(bool isLeft , const QString& text)
{
    MessageContentLabel* textMessageItem = new MessageContentLabel(text,isLeft,model::TEXT_TYPE,"",QByteArray());
    return textMessageItem;
}

//////////////////////////////////////////////////
////// 创建类表示"文本消息"正文部分
//////////////////////////////////////////////////

MessageContentLabel::MessageContentLabel(const QString &text, bool isLeft,model::MessageType messageType,const QString& fileId,
                                         const QByteArray& content)

    :isLeft(isLeft),messageType(messageType),fileId(fileId),content(content)
{
    //设置一下SizePolicy
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

    QFont font;
    font.setFamily("微软雅黑");
    font.setPixelSize(16);

    this->label = new QLabel(this);
    this->label->setText(text);
    this->label->setFont(font);
    this->label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
    this->label->setWordWrap(true); //设置自动换行
    this->label->setStyleSheet("QLabel {padding: 0 10px;line-height: 1.2;background-color: transparent;}");

    //针对文件消息,并且content为空的情况下，通过网络加载数据
    if(messageType == model::TEXT_TYPE){
        return;
    }

    if(this->content.isEmpty()){
        model::DataCenter* dataCenter = model::DataCenter::getInstance();
        connect(dataCenter,&model::DataCenter::getSingleFileDone,this,&MessageContentLabel::updateUI);
        dataCenter->getSingleFileAsync(this->fileId);
    }else{
        //content不为空,说明当前的这个数据就是已经现成的,直接就把表示加载的状态设为true
        this->loadContentDone = true;
    }
}

//这个函数会被该控件被显示的时候自动调用到
void MessageContentLabel::paintEvent(QPaintEvent *event)
{
    (void)event;
    //1.获取父元素的宽度
    QObject* object = this->parent();
    if(!object->isWidgetType())
    {
        //当前这个对象的父元素不是预期的QWidget,此时不需要进行任何后续的绘制操作
        return ;
    }
    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.6;

    //2.计算当前文本，如果是一行放置，需要多宽
    QFontMetrics metrics(this->label->font());
    int totalWidth = metrics.horizontalAdvance(this->label->text());

    //3.计算此处的行数是多少（40表示左右各有20px的边
    int rows = (totalWidth / (width - 40)) + 1;
    if(rows == 1)
    {
        //如果此时得到的行数只有一行
        width = totalWidth + 40;
    }

    //4.根据行数，计算得到高度（20表示上下各有10px的边距）
    int height = rows * (this->label->font().pixelSize() * 1.2) + 20;

    //5.绘制圆角矩形和箭头
    QPainter painter(this);
    QPainterPath path;
    //设置抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    if(isLeft)
    {
        //设置刷子和画笔的颜色
        painter.setPen(QPen(QColor(255,255,255)));
        painter.setBrush(QColor(255,255,255));

        //绘制圆角矩形
        painter.drawRoundedRect(10,0,width,height,10,10);
        //绘制箭头
        path.moveTo(10,15);
        path.lineTo(0,20);
        path.lineTo(10,25);
        path.closeSubpath()	;//绘制的线条形成真正的多边形，才能进行Brush填充颜色
        painter.drawPath(path);

        this->label->setGeometry(10,0,width,height);
    }
    else
    {
        painter.setPen(QPen(QColor(137,217,97)));
        painter.setBrush(QColor(137,217,91));

        //设置圆角矩形左侧边的横坐标
        int leftPos = this->width() - width - 10;//10是容纳箭头的宽度
        //圆角矩形右侧的横坐标位置
        int rightPos = this->width() - 10;
        //绘制圆角矩形
        painter.drawRoundedRect(leftPos,0,width,height,10,10);
        //绘制箭头
        path.moveTo(rightPos,15);
        path.lineTo(rightPos + 10,20);
        path.lineTo(rightPos,25);
        path.closeSubpath();
        painter.drawPath(path);

        this->label->setGeometry(leftPos,0,width,height);
    }

    //6.重新设置父元素的高度，确保父元素足够高，能够容纳上述绘制消息显示的区域
    parent->setFixedHeight(height + 50);
}

void MessageContentLabel::mousePressEvent(QMouseEvent *event)
{
    //实现鼠标点击之后，触发文件另存为
    if(event->button() == Qt::LeftButton){
        //左键按下
        if(this->messageType == model::FILE_TYPE){
            //真正触发另存为
            if(!this->loadContentDone){
                Toast::showMessage("数据尚未加载成功,请稍后重试");
                return;
            }
            saveAsFile(this->content);
        }else if(this->messageType == model::SPEECH_TYPE){
            if(!this->loadContentDone){
                Toast::showMessage("数据尚未加载成功,请稍后重试");
                return;
            }
            SoundRecorder* soundRecorder = SoundRecorder::getInstance();
            this->label->setText("播放中...");
            connect(soundRecorder,&SoundRecorder::soundPlayDone,this,&MessageContentLabel::playDone,Qt::UniqueConnection);
            soundRecorder->startPlay(this->content);
        }else{
            //其他消息,比如普通文本消息,不做处理
        }
    }
}

void MessageContentLabel::updateUI(const QString &fileId, const QByteArray &fileContent)
{
    //也和刚才图片消息一样，就需要判定收到的数据属于哪个fileId的
    if(fileId != this->fileId){
        return;
    }

    this->content = fileContent;
    this->loadContentDone = true;

    //对于文件消息来说，要在界面上显示"[文件]test.txt"这样的形式,这个内容和文件content无关
    //在从服务器拿到文件正文之前，界面内容应该就是绘制好了,此时拿到正文之后,界面应该也不必做出任何实质性的调整
    this->update();
}

void MessageContentLabel::saveAsFile(const QByteArray &content)
{
    //弹出对话框,让用户选择路径
    QString filePath = QFileDialog::getSaveFileName(this,"另存为",QDir::homePath(),"*");
    if(filePath.isEmpty()){
        LOG() << "用户取消了文件另存为";
        return;
    }
    model::writeByteArraytoFile(content,filePath);
}

void MessageContentLabel::playDone()
{
    if(this->label->text() == "播放中..."){
        this->label->setText("[语音]");
    }
}

void MessageContentLabel::contextMenuEvent(QContextMenuEvent *event)
{
    if(messageType != model::SPEECH_TYPE){
        LOG() << "非语音消息暂时不支持右键菜单";
        return;
    }

    QMenu* menu = new QMenu(this);
    QAction* action = menu->addAction("语音转文字");
    connect(action,&QAction::triggered,this,[=](){
        model::DataCenter* dataCenter = model::DataCenter::getInstance();
        connect(dataCenter,&model::DataCenter::speechConvertTextDone,this,&MessageContentLabel::speechConvertTextDone,Qt::UniqueConnection);
        dataCenter->speechConvertTextAsync(this->fileId,this->content);
    });

    //此处弹出"模态对话框"显示菜单/菜单项.exec会在用户进一步操作之前,阻塞
    menu->exec(event->globalPos());
    delete menu;
}

void MessageContentLabel::speechConvertTextDone(const QString &fileId, const QString &text)
{
    if(this->fileId != fileId){
        //直接跳过,此时的结果不是针对这一条语音消息的结果
        return;
    }
    //修改界面内容
    this->label->setText("[语音转文字]" + text);
    this->update();
}

//////////////////////////////////////////////////
////// 创建类表示"图片消息"正文部分
//////////////////////////////////////////////////
MessageImageLabel::MessageImageLabel(const QString &fileId, const QByteArray &content, bool isLeft)
    :fileId(fileId),content(content),isLeft(isLeft)
{
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

    imageBtn = new QPushButton(this);
    imageBtn->setStyleSheet("QPushButton {border: none;}");

    if(content.isEmpty()){
        //此处这个控件,是针对"从服务器拿到图片消息"这种情况
        //拿着fileId,去服务器获取图片内容
        model::DataCenter* dataCenter = model::DataCenter::getInstance();
        connect(dataCenter,&model::DataCenter::getSingleFileDone,this,&MessageImageLabel::updateUI);
        dataCenter->getSingleFileAsync(fileId);
    }


}

void MessageImageLabel::updateUI(const QString& fileId,const QByteArray& content)
{
    if(this->fileId != fileId){
        //没对上fileId,当前响应的图片是其他的图片消息请求的
        return;
    }
    //对上了,真正显示图片内容
    this->content = content;

    //进行绘制图片到界面上的操作
    this->update();
}

//真正进行绘制图片到界面上的操作
void MessageImageLabel::paintEvent(QPaintEvent *event)
{
    (void)event;
    //1.先拿到该元素的父元素,看父元素的宽度是多少
    //	此处显示的图片宽度的上限是父元素的60%
    QObject* object = this->parent();
    if(!object->isWidgetType()){
        //这个逻辑理论上是不会存在的
        return;
    }

    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.6;

    //2.加载二进制数据为图片对象
    QImage image;
    if(content.isEmpty()){
        //此时图片的响应数据还没回来
        //此处先拿到"固定默认图片"顶替一下
        QByteArray tmpContent = model::loadFiletoByteArray(":/resource/image/image.png");
        image.loadFromData(tmpContent);
    }else{
        //此处的load操作QImage能够自动识别当前图片是啥类型的(png,jpg...)
        image.loadFromData(content);
    }

    //3.针对图片进行
    int height = 0;
    if(image.width() > width){
        //发现图片更宽就需要把图片缩放一下，使用width作为实际的宽度
        //等比例缩放
        height = ((double)image.height() / image.width()) * width;
    }else{
        //图片本身不太宽，不需要缩放
        width = image.width();
        height = image.height();
    }

    //pixmap只是一个中间变量,QImage不能直接转成QIcon,需要QPixmap中转一下
    QPixmap pixmap = QPixmap::fromImage(image);
    imageBtn->setIconSize(QSize(width,height));
    imageBtn->setIcon(QIcon(pixmap));

    //4.由于图片高度是计算算出来的,该元素的父元素的高度,能够容纳当前的元素
    //此处的+50是为了能够容纳下上方的"名字"部分,同时留下一点冗余空间
    parent->setFixedHeight(height + 50);

    //5.确定按钮所在的位置
    //左侧消息,和右侧消息，要显示的位置是不同的
    if(isLeft){
        imageBtn->setGeometry(10,0,width,height);
    }else{
        int leftPos = this->width() - width - 10;
        imageBtn->setGeometry(leftPos,0,width,height);
    }

}






